.. _Массивы:

Массивы
=======

.. index::
    Массивы


Мы определили 5 переменных в одном из упражнений в предыдущей главе и использовали их для вычислений.
Определение переменных могло выглядеть следущим образом:

.. code-block:: d

    double value_1;
    double value_2;
    double value_3;
    double value_4;
    double value_5;

Этот метод определения переменных по отдельности плохо масштабируется, для случая, когда требуется еще
большое количество переменных. Представьте, что вам нужна тысяча значений; практически
невозможно определить тысячу переменных от value_1 до value_1000 .
Массивы полезны в таких случаях: функция array позволяет нам определить одну
переменную, которая хранит несколько значений вместе. Несмотря на простоту, массивы являются
наиболее распространенной структурой данных, используемой для хранения набора значений.
В этой главе рассматриваются только некоторые функции массивов. Дополнительные функции будут
представлено позже в главе о срезах и других функциях массива (стр. ??).

Определение
------------

Определение массива (иначе говоря : переменной типа массив) очень похоже на ороеделение обычной переменной,
лишь с той разницей, что мы указываем в квадратных скобках количество значений, которые будут содержаться в массиве, или другими словами : длину массива.
Сравните два определения

.. code-block:: d

    int singleValue;
    int[10] arrayOfTenValues;

Первая строка в приведенном выше примере  - это определение переменной, которая хранит одно значение, точно
так же, как переменные, которые мы определили до сих пор. Вторая строка - это определение
переменной, которая хранит десять последовательных значений. Другими словами, она хранит массив из
десяти целых значений. Вы также можете думать об этом как об определении десяти переменных одного
типа или, для краткости, как об определении массива.
Соответственно, эквивалент пяти отдельных переменных, приведенных выше, может быть определен
как массив из пяти значений, используя следующий синтаксис:

.. code-block:: d

    double[5] values;


Это определение может быть прочитано как 5 значений типа double. Обратите внимание, что я выбрал имя
переменной массива во множественном числе, чтобы избежать путаницы с обычной переменной,содержащей одно значение.
Переменные, которые хранят только одно значение, называются скалярными переменными.
Таким образом, определение переменной массива состоит из типа
значений, количества значений и имени переменной, которая ссылается на массив
значений:

.. code-block:: d

    type_name[value_count] variable_name;

The type of the values can also be a user-defined type. (We will see user-defined
types later.) For example:


.. code-block:: d

    // Масиив содержит информацию о погоде в городах Here, the bool values may mean
    // где the bool значение означает
    // false: облачность
    // true : солнечно
    //
    bool[cityCount] weatherConditions;
    // Массив содержит информацию о весе 100 коробок
    double[100] boxWeights;
    // Массив хранит информацию о студентах колледжа
    StudentInformation[studentCount] studentInformation;

Контейнеры и элементы
------------------------

Структуры данных, объединяющие элементы определенного типа, называются
контейнерами. Согласно этому определению, массивы являются контейнерами. Например,
массив, содержащий значения температуры воздуха за июльские дни, может объединить 31
значение double и сформировать контейнер из элементов типа double .
Переменные контейнера называются элементами. Количество элементов
массива называется длиной массива.

Доступ к элементам
------------------

Чтобы различать переменные в упражнении предыдущей главы, мы
добавили символ подчеркивания и число к их именам, как в value_1 . Это
невозможно и не обязательно, когда в одном массиве хранятся все значения под одним
именем. Вместо этого доступ к элементам осуществляется путем указания номера элемента в
квадратных скобках:

.. code-block:: d

    values[0];

Это выражение может быть прочитано как элемент с номером 0 массива с именем
values. Другими словами, вместо ввода value_1 необходимо ввести values[0] с
массивами.
Здесь стоит отметить два важных момента:

#. Нумерация элементов массива начинается с нуля: хотя люди привыкли присваивать номера элементам,
   начиная с 1. Значения, которые мы ранее пронумеровали бы как 1, 2, 3, 4 и 5, в массиве будут пронумерованы как 0, 1, 2, 3 и 4.
   Это изменение может сбить с толку начинающих программистов.

#. Two different uses of the [] characters: Don't confuse the two separate uses
of the [] characters. When defining arrays, the [] characters are written after
the type of the elements and specify the number of elements. When accessing
elements, the [] characters are written after the name of the array and
specify the number of the element that is being accessed:

Cимволов [] используются двояко: не путайте два отдельных использования
символов []. При определении массивов символы [] пишутся после
типа элементов и указывают количество элементов. При
обращении к элементам символы [] записываются после имени массива и
указывают номер элемента, к которому осуществляется доступ:

.. code-block:: d

    // Определение массива Массив содержит 12 элементов .
    // Массив используется для хранения количества дней в каждом месяце
    int[12] monthDays;
    // Доступ. Устанавливаем оличество дней в декабре
    monthDays[11] = 31;
    // Печатаем количество дней в январе
    writeln("В январе ", monthDays[0], " дней.");

Напоминаем: нумерация идет начиная с нуля: Январь - 0, Декабрь 11

Индексы
--------

.. index::
    [
    ]

Номер элемента в массивеназывается индексом и используется для получения доступа к элементу.
Индекс не обязательно должен быть константой, переменные то же могут быть использованы в качестве индеска, что делает массив более полезным
Например используем переменную monthIndex

.. code-block:: d

    writeln("This month has ", monthDays[monthIndex], " days.");


Если значение индекса месяца равно 2, приведенное выше выражение выведет значение
monthDays[2], количество дней в марте. Допустимы только значения индекса от нуля до единицы, меньшие длины массива.
Например, допустимыми индексами массива из трех элементов являются 0, 1 и 2.
Доступ к массиву с недопустимым индексом приводит к завершению работы программы с ошибкой.
Массивы - это контейнеры, в которых элементы размещаются бок о бок в
памяти компьютера. Например, элементы массива, содержащие число
количество дней в каждом месяце может быть показано следующим образом (при условии, что в феврале
28 дней):

.. code-block:: console

    indexes → 0 1 2 3 4 5 6 7 8 9 10 11
    elements → 31 | 28 | 31 | 30 | 31 | 30 | 31 | 31 | 30 | 31 | 30 | 31 |

*Note:  Значения индексов - демострационные. Это не значит, что они именно так хранятся в памяти
компьютера*

Элемент с индексом 0 - количество дней в Январе. Элемент с индексом 1 - количество дней в феврале

Массивы с фиксированной длиной и динамические массивы
-----------------------------------------------------

Если размер массива указан при написании программы, то это массив фиксированного размерa
Если размер может изменяться во время выполнения программы, то это динамический массив.
Оба массива, которые мы определили выше, являются массивами фиксированного размера, поскольку
количество их элементов указано как 5 и 12 на момент написания программы. Размер этих массивов не может быть изменё во время выполнения
программы. Чтобы изменить их длину, необходимо изменить исходный код и перекомпилировать программу.
Определение динамических массивов проще, чем определение массивов фиксированного размера. Если размер массива не указан, то создаётся динамический массив

.. code-block:: d

    int[] dynamicArray;


Размер подобного массива может уменьшаться и увеличиваться во время работы программы
Массивы фиксированной длины известны также под названием статические массивы.

Использования свойтва .length для получения или установки размера массива
-------------------------------------------------------------------------

Тип массивы имеет набор свойств, сейчас мы рассмотри одно из них .length,
 .length возращает количество элементов в массиве

.. code-block:: d

    writeln("The array has ", array.length, " elements.");


Размер динамического массива может быть изменён присвоением другой величины свойству .length

.. code-block:: d

   int[] array;  //изначально массив пустой
   array.length = 5; // теперь длина равна 5

An array example
-----------------

Пересмотрим один из наших примеров, где использовались отдельные переменные и перепишем его с использованием массивов


.. code-block:: d

    import std.stdio;

    void main()
    {
        // переменная будет использована как счетчик цикла
        int counter;
        // Определяем массив, содержащий 5 элементов типа double
        double[5] values;
        // Читаем массив в цикле
        while (counter < values.length)
        {
            write("Value ", counter + 1, ": ");
            readf(" %s", &values[counter]);
            ++counter;
        }
        writeln("Twice the values:");
        counter = 0;
        while (counter < values.length)
        {
            writeln(values[counter] * 2);
            ++counter;
        }
    }


Наблюдения: Значение счетчика определяет, сколько раз циклы
повторяются (итерируются). Выполняется итерация цикла, пока его значение меньше значений.длина
гарантирует, что циклы выполняются один раз для каждого элемента. Поскольку значение этой переменной
увеличивается в конце каждой итерации, выражение values[counter]
ссылается на элементы массива один за другим: значения[0] , значения[1] и т.д.
Чтобы увидеть, чем эта программа лучше предыдущей, представьте, что вам нужно
прочитать 20 значений. Приведенная выше программа потребует единственного изменения: замены 5
с 20. С другой стороны, программа, которая не использовала массив, должна была бы
иметь 20 определений переменных. Кроме того, поскольку вы не смогли бы использовать
цикл для перебора 20 значений, вам также пришлось бы повторить несколько строк 20 раз,
по одному разу для каждой однозначной переменной.


Инициализация массива
---------------------

Как и любая переменная в D, элементы массивов инициализируются автоматически.
Начальное значение элементов зависит от типа элементов: 0 для int ,
double.nan для double и т.д.
Все элементы массива значений, приведенного выше, инициализируются double.nan :

.. code-block:: d

    double[5] values; // elements are all double.nan

Очевидно, что значения элементов могут быть изменены позже во время выполнения
программы. Мы уже видели это выше при присвоении элементу массива:

.. code-block:: d

    monthDays[11] = 31;

Это также можно сделать при считывании входных данных:

.. code-block:: d

    readf(" %s", &values[counter]);


Иногда желаемые значения элементов известны во время определения
массива. В таких случаях начальные значения элементов могут быть указаны
в правой части оператора присваивания, в квадратных скобках. Давайте
посмотрим на это в программе, которая считывает номер месяца у пользователя и
выводит количество дней в этом месяце:


.. code-block:: d

    import std.stdio;

    void main()
    {
        // Assuming that February has 28 days
        int[12] monthDays =
            [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        write("Please enter the number of the month: ");
        int monthNumber;
        readf(" %s", &monthNumber);
        int index = monthNumber - 1;
        writeln("Month ", monthNumber, " has ",
            monthDays[index], " days.");
    }


Как вы можете видеть, массив month Days определен и инициализирован одновременно.
Также обратите внимание, что номер месяца, который находится в диапазоне 1-12, преобразуется в
допустимый индекс массива в диапазоне 0-11. Любое значение, введенное за пределами диапазона 1-12
, приведет к завершению работы программы с ошибкой.
При инициализации массивов можно использовать одно значение в правой
части. В этом случае все элементы массива инициализируются этим значением:

.. code-block:: d

    int[10] allOnes = 1; // All of the elements are set to 1

Базовые операции над массивами
------------------------------

Массивы обеспечивают ряд операций, которые выполняются на всеми элементами массива

**Копирование массива фиксированной длины**

Оператор присваивания копирует все элементы массива из правой части в массив в левой части выражения

.. code-block:: d

        int[5] source = [ 10, 20, 30, 40, 50 ];
        int[5] destination;
        destination = source;

*Примечание: Значение оператор присваивания для динамических массивов кардинально отличается*

**Adding elements to dynamic arrays**

Оператор  ~= добавляет новый элемент в динамический массив

.. code-block:: d

    int[] array          // пустой массив
    array ~= 7;          // теперь массив выглядит как [7]
    array ~= 360;        // теперь массив выглядит как [7, 360]
    array ~= [ 30, 40 ]; // теперь массив выглядит как [7, 360, 30, 40]



Вместе с тем запрещено добавлять элементы из массива с фиксированной длиной

.. code-block:: d

    int[10] array;
    array ~= 7;


**Удаление елемента из динамического массива**

Элементы массива могут быть удалены с помощью функции remove(), которая определена в модуле std.algorithm.
Поскольку для одних и тех же элементов может быть более одного среза, функция remove() фактически не может изменить номер элемента массива.
Скорее, он должен переместить некоторые элементы массива на одну или несколько позиций влево. По этой причине результат операции удаления должен быть присвоен обратно
той же переменной массива.

Имеют место два способа использования функции remove

#. Передать функции индекс удаляемого элемента

   .. code-block:: d

        import std.stdio;
        import std.algorithm;

        void main()
        {
            int[] array = [10, 20, 30, 40];
            array = array.remove(1);
            writeln(array);
        }



   .. code-block:: console

        [10, 30, 40]


#. Специфицировать удаляемый элемент посредством lambda функции. lambda функции - будут рассмотрены позже (ссылка на страницу)
   Например удаляем все элементы массива, которые равны 42

   .. code-block:: d

        import std.stdio;
        import std.algorithm;

        void main()
        {
            int[] array = [10, 42, 20, 30, 42, 40];
            array = array.remove!(a => a == 42);
            // Assigned back to array
            writeln(array);
        }


    .. code-block:: console

        [10, 20, 30, 40]

**Конкатенация массивов**

Оператор ~ создает новый массив путем объединения двух массивов. Его аналог ~=
объединяет два массива и присваивает результат обратно левостороннему массиву:

.. code-block:: d

    import std.stdio;

    void main()
    {
        int[10] first = 1;
        int[10] second = 2;
        int[] result;
        result = first ~ second;
        writeln(result.length); // prints 20
        result ~= first;
        writeln(result.length); // prints 30
    }


Оператор ~= не может быть использован для слияния с массивом с фиксированной длиной

.. code-block:: d

    int[20] result;
    // ...
    result ~= first;   // ← compilation ERROR


**Сортировка массива**

std.algorithm.sort can sort the elements of many types of collections. In the
case of integers, the elements get sorted from the smallest value to the greatest
value. In order to use the sort() function, one must import the std.algorithm
module first. (We will see functions in a later chapter.)

Функция sort определённая в модуле std.algorithm   может быть использована для сортировки коллекций разных типов.
В случае цулых чисел элементы сортируются в порядке возрастания. Для использования этой функции ваша программа должна импортировать
модуль std.algorithm  (мы рассмотрим эти функции позднее)

 .. code-block:: d

    import std.stdio;
    import std.algorithm;

    void main()
    {
        int[] array = [4, 3, 1, 5, 2];
        sort(array);
        writeln(array);
    }


 Вывод

.. code-block:: console

    [1, 2, 3, 4, 5]


**Реверсирование массива**

Функция reverse определенная в модуле std.algorithm меняет порядок следования элементов массива на противоположный
Новый массив не создаётся

.. code-block:: d

    import std.stdio;
    import std.algorithm;

    void main()
    {
        int[] array = [4, 3, 1, 5, 2];
        reverse(array);
        writeln(array);
    }


Вывод

.. code-block::  console

    [2, 5, 1, 3, 4]


Упражнение
----------

#.  Напишите программу, которая запрашивает у пользователя, сколько значений будет введено, а затем
    считывает их все. Попросите программу отсортировать элементы с помощью sort(), а затем
    перевернуть отсортированные элементы с помощью reverse() .
#.  Напишите программу, которая считывает числа из входных данных и печатает нечетные и
    четные единицы отдельно, но по порядку. Обработайте значение -1 специально для определения
    конца чисел; не обрабатывайте это значение.
    Например
    Ввод : 1 4 7 2 3 8 11 -1
    Вывод : 1 3 7 11 2 4 8

    Подсказка: Возможно, вы захотите поместить элементы в отдельные массивы. Вы можете определить,
    является ли число нечетным или четным, используя оператор % (остаток).

#.  Ниже приведена программа, которая работает не так, как ожидалось. Программа
    написана для считывания пяти чисел из входных данных и записи квадратов этих
    чисел в массив. Затем программа пытается напечатать квадраты на
    выходе. Вместо этого программа завершается с ошибкой.
    Исправьте ошибки в этой программе и заставьте ее работать должным образом:


.. code-block:: d

    import std.stdio;

    void main()
    {
        int[5] squares;
        writeln("Please enter 5 numbers");
        int i = 0;
        while (i <= 5)
        {
            int number;
            write("Number ", i + 1, ": ");
            readf(" %s", &number);
            squares[i] = number * number;
            ++i;
        }

        writeln("=== The squares of the numbers ===");
        while (i <= squares.length)
        {
            write(squares[i], " ");
            ++i;
        }
        writeln();
    }



решения :ref:`для главы массивы<решения для главы массивы>`

